using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Dynamics;
using Umbraco.Tests.TestHelpers;

namespace Umbraco.Tests.PublishedContent
{
	[TestFixture]
    public abstract class DynamicDocumentTestsBase<TDocument, TDocumentList> : PublishedContentTestBase
	{
        protected override DatabaseBehavior DatabaseTestBehavior
        {
            get { return DatabaseBehavior.NoDatabasePerFixture; }
        }

		protected override string GetXmlContent(int templateId)
		{
			return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[ 
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT CustomDocument ANY>
<!ATTLIST CustomDocument id ID #REQUIRED>
]>
<root id=""-1"">
	<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc="""">
		<content><![CDATA[]]></content>
		<umbracoUrlAlias><![CDATA[this/is/my/alias, anotheralias]]></umbracoUrlAlias>
		<umbracoNaviHide>1</umbracoNaviHide>
        <siteTitle><![CDATA[This is my site]]></siteTitle>
		<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
			<content><![CDATA[<div>This is some content</div>]]></content>
			<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>			
			<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc="""">
				<content><![CDATA[]]></content>
				<umbracoUrlAlias><![CDATA[only/one/alias]]></umbracoUrlAlias>
				<creatorName><![CDATA[Custom data with same property name as the member name]]></creatorName>
			</Home>			
			<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
			<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
            <Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""">
				<content><![CDATA[some content]]></content>
				<blah><![CDATA[some content]]></blah>
                <umbracoNaviHide>1</umbracoNaviHide>
			</Home>
		</Home>
		<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
		</Home>
		<CustomDocument id=""4444"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,4444"" isDoc="""">
			<selectedNodes><![CDATA[1172,1176,1173]]></selectedNodes>
			<CustomDocument id=""5555"" parentID=""1046"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""0"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,4444,5555"" isDoc="""">
			</CustomDocument>
		</CustomDocument>
	</Home>
	<CustomDocument id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
</root>";
		}

		/// <summary>
		/// Returns the dynamic node/document to run tests against
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		protected abstract dynamic GetDynamicNode(int id);

        [Test]
        public void Recursive_Property()
        {
            var doc = GetDynamicNode(1174);
            var prop = doc.GetProperty("siteTitle", true);
            Assert.IsNotNull(prop);
            Assert.AreEqual("This is my site", prop.Value);
            prop = doc.GetProperty("_siteTitle"); //test with underscore prefix
            Assert.IsNotNull(prop);
            Assert.AreEqual("This is my site", prop.Value);
            Assert.AreEqual("This is my site", doc._siteTitle);
        }

        /// <summary>
        /// Tests the internal instance level caching of returning properties
        /// </summary>
        /// <remarks>
        /// http://issues.umbraco.org/issue/U4-1824
        /// http://issues.umbraco.org/issue/U4-1825
        /// </remarks>
        [Test]
        public void Can_Return_Property_And_Value()
        {
            var doc = GetDynamicNode(1173);

            Assert.IsTrue(doc.HasProperty(Constants.Conventions.Content.UrlAlias));
            var prop = doc.GetProperty(Constants.Conventions.Content.UrlAlias);
            Assert.IsNotNull(prop);
            Assert.AreEqual("page2/alias, 2ndpagealias", prop.Value);
            Assert.AreEqual("page2/alias, 2ndpagealias", doc.umbracoUrlAlias);
        }

        /// <summary>
        /// Tests the IsLast method with the result set from a Where statement
        /// </summary>
        [Test]
        public void Is_Last_From_Where_Filter()
        {
            var doc = GetDynamicNode(1173);

            foreach (var d in doc.Children.Where("Visible"))
            {
                if (d.Id != 1178)
                {
                    Assert.IsFalse(d.IsLast());
                }
                else
                {
                    Assert.IsTrue(d.IsLast());
                }
            }

        }

		[Test]
		public void Single()
		{
			var doc = GetDynamicNode(4444);

			var result = doc.Children().Single();				

			Assert.IsNotNull(result);
			Assert.AreEqual(5555, result.Id);
		}

		[Test]
		public void Single_With_Query()
		{
			var doc = GetDynamicNode(1046);

			var result = doc.Children().Single("id==1175");

			Assert.IsNotNull(result);
			Assert.AreEqual(1175, result.Id);
		}

		[Test]
		public void First()
		{
			var doc = GetDynamicNode(1173);

			var result = doc.Children().First();

			Assert.IsNotNull(result);
			Assert.AreEqual(1174, result.Id);
		}

		[Test]
		public void First_With_Query()
		{
			var doc = GetDynamicNode(1173);

			var result = doc.Children().First("blah==\"some content\"");

			Assert.IsNotNull(result);
			Assert.AreEqual(1176, result.Id);
		}

		[Test]
		public void Where_User_Property_Value()
		{
			var doc = GetDynamicNode(1173);

			var result = (IEnumerable<dynamic>)doc.Children().Where("blah==\"some content\"");

			Assert.IsNotNull(result);
			Assert.AreEqual(1, result.Count());
			Assert.AreEqual(1176, result.Single().Id);		
		}

		[Test]
		public void String_ContainsValue_Extension_Method()
		{
			var doc = GetDynamicNode(1046);

			var paramVals = new Dictionary<string, object> { { "searchId", 1173 } }; //this is an integer value
			var result = doc.Children()
				.Where("selectedNodes.ContainsValue(searchId)", paramVals) //call an extension method
				.FirstOrDefault();

			Assert.IsNotNull(result);
			Assert.AreEqual(4444, result.Id);

			//don't find!
			paramVals = new Dictionary<string, object> { { "searchId", 1111777 } };
			result = doc.Children()
				.Where("selectedNodes.ContainsValue(searchId)", paramVals)
				.FirstOrDefault();

			Assert.IsNotNull(result);
			Assert.IsTrue(result.GetType() == typeof(DynamicNull) || result.GetType() == typeof(umbraco.MacroEngines.DynamicNull));
			//Assert.AreEqual(typeof(DynamicNull), result.GetType());
		}

		[Test]
		public void String_Contains_Method()
		{
			var doc = GetDynamicNode(1046);

			var paramVals = new Dictionary<string, object> { { "searchId", "1173" } };
			var result = doc.Children()
				.Where("selectedNodes.Contains(searchId)", paramVals)
				.FirstOrDefault();

			Assert.IsNotNull(result);
			Assert.AreEqual(4444, result.Id);

			//don't find!
			paramVals = new Dictionary<string, object> { { "searchId", "1aaa173" } };
			result = doc.Children()
				.Where("selectedNodes.Contains(searchId)", paramVals)
				.FirstOrDefault();

			Assert.IsNotNull(result);
			Assert.IsTrue(result.GetType() == typeof (DynamicNull) || result.GetType() == typeof (umbraco.MacroEngines.DynamicNull));
			//Assert.AreEqual(typeof (DynamicNull), result.GetType());
		}

		[Test]
		public void String_Split_Method()
		{
			var doc = GetDynamicNode(1046);

			var paramVals = new Dictionary<string, object>
				{
					{ "splitTerm", new char[] { ',' } },
					{ "splitOptions", StringSplitOptions.RemoveEmptyEntries }
				};
			var result = doc.Children()
				.Where("selectedNodes.Split(splitTerm, splitOptions).Length == 3", paramVals)
				.FirstOrDefault();

			Assert.IsNotNull(result);
			Assert.AreEqual(4444, result.Id);
		}

        [Ignore("We are ignoring this test because currently our ExpressionParser class cannot deal with this... it needs some serious TLC but it is very complex.")]
		[Test]
		public void Complex_Linq()
		{
			var doc = GetDynamicNode(1173);

			var paramVals = new Dictionary<string, object> {{"splitTerm", new char[] {','}}, {"searchId", "1173"}};
			var result = doc.Ancestors().OrderBy("level")
				.Single()
				.Descendants()
				.Where("selectedNodes != null && selectedNodes != String.Empty && selectedNodes.Split(splitTerm).Contains(searchId)", paramVals)
				.FirstOrDefault();

			Assert.IsNotNull(result);
			Assert.AreEqual(4444, result.Id);	
		}

		[Test]
		public void Index()
		{
			var doc = GetDynamicNode(1173);
			Assert.AreEqual(0, doc.Index());
			doc = GetDynamicNode(1176);
			Assert.AreEqual(3, doc.Index());
			doc = GetDynamicNode(1177);
			Assert.AreEqual(1, doc.Index());
			doc = GetDynamicNode(1178);
			Assert.AreEqual(2, doc.Index());
		}

		[Test]
		public virtual void Is_First_Root_Nodes()
		{
			var doc = GetDynamicNode(1046); //test root nodes
			Assert.IsTrue(doc.IsFirst());
			doc = GetDynamicNode(1172);
		    Assert.IsFalse(doc.IsFirst());
		}

        [Test]
        public void Is_First()
        {
            var doc = GetDynamicNode(1173); //test normal nodes
            Assert.IsTrue(doc.IsFirst());
            doc = GetDynamicNode(1175);
            Assert.IsFalse(doc.IsFirst());
        }

        [Test]
        public virtual void Is_Not_First_Root_Nodes()
        {
            var doc = GetDynamicNode(1046); //test root nodes
            Assert.IsFalse(doc.IsNotFirst());
            doc = GetDynamicNode(1172);
            Assert.IsTrue(doc.IsNotFirst());
        }

		[Test]
		public void Is_Not_First()
		{		
			var doc = GetDynamicNode(1173); //test normal nodes
			Assert.IsFalse(doc.IsNotFirst());
			doc = GetDynamicNode(1175);
			Assert.IsTrue(doc.IsNotFirst());
		}

        [Test]
        public virtual void Is_Position_Root_Nodes()
        {
            var doc = GetDynamicNode(1046); //test root nodes
            Assert.IsTrue(doc.IsPosition(0));
            doc = GetDynamicNode(1172);
            Assert.IsTrue(doc.IsPosition(1));            
        }

		[Test]
		public void Is_Position()
		{
			var doc = GetDynamicNode(1173); //test normal nodes
			Assert.IsTrue(doc.IsPosition(0));
			doc = GetDynamicNode(1175);
			Assert.IsTrue(doc.IsPosition(1));
		}

		[Test]
		public void Children_GroupBy_DocumentTypeAlias()
		{
			var doc = GetDynamicNode(1046);

			var found1 = doc.Children.GroupBy("DocumentTypeAlias");

			var casted = (IEnumerable<IGrouping<object, dynamic>>)(found1);
			Assert.AreEqual(2, casted.Count());
			Assert.AreEqual(2, casted.Single(x => x.Key.ToString() == "Home").Count());
			Assert.AreEqual(1, casted.Single(x => x.Key.ToString() == "CustomDocument").Count());
		}

		[Test]
		public void Children_Where_DocumentTypeAlias()
		{
			var doc = GetDynamicNode(1046);

			var found1 = doc.Children.Where("DocumentTypeAlias == \"CustomDocument\"");
			var found2 = doc.Children.Where("DocumentTypeAlias == \"Home\"");

			Assert.AreEqual(1, found1.Count());
			Assert.AreEqual(2, found2.Count());
		}

		[Test]
		public void Children_Where_NodeTypeAlias()
		{
			var doc = GetDynamicNode(1046);

			var found1 = doc.Children.Where("NodeTypeAlias == \"CustomDocument\"");
			var found2 = doc.Children.Where("NodeTypeAlias == \"Home\"");

			Assert.AreEqual(1, found1.Count());
			Assert.AreEqual(2, found2.Count());
		}

		[Test]
		public void Children_Order_By_Update_Date()
		{
			var asDynamic = GetDynamicNode(1173);

			var ordered = asDynamic.Children.OrderBy("UpdateDate");
			var casted = (IEnumerable<TDocument>)ordered;

			var correctOrder = new[] { 1178, 1177, 1174, 1176 };
			for (var i = 0; i < correctOrder.Length ;i++)
			{
				Assert.AreEqual(correctOrder[i], ((dynamic)casted.ElementAt(i)).Id);
			}

		}

		[Test]
		public void Children_Order_By_Update_Date_Descending()
		{
			var asDynamic = GetDynamicNode(1173);

			var ordered = asDynamic.Children.OrderBy("UpdateDate desc");
			var casted = (IEnumerable<TDocument>)ordered;

			var correctOrder = new[] { 1176, 1174, 1177, 1178 };
			for (var i = 0; i < correctOrder.Length; i++)
			{
				Assert.AreEqual(correctOrder[i], ((dynamic)casted.ElementAt(i)).Id);
			}

		}

		[Test]
		public void HasProperty()
		{
			var asDynamic = GetDynamicNode(1173);

			var hasProp = asDynamic.HasProperty(Constants.Conventions.Content.UrlAlias);

			Assert.AreEqual(true, (bool)hasProp);

		}

		[Test]
		public void Skip()
		{
			var asDynamic = GetDynamicNode(1173);

			var skip = asDynamic.Children.Skip(2);
			var casted = (IEnumerable<TDocument>)skip;

			Assert.AreEqual(2, casted.Count());
		    Assert.IsTrue(casted.Select(x => ((dynamic) x).Id).ContainsAll(new dynamic[] {1178, 1176}));

		}

		[Test]
		public void HasValue()
		{
			var asDynamic = GetDynamicNode(1173);

			var hasValue = asDynamic.HasValue(Constants.Conventions.Content.UrlAlias);
			var noValue = asDynamic.HasValue("blahblahblah");

			Assert.IsTrue(hasValue);
			Assert.IsFalse(noValue);
		}

		[Test]
		public void Take()
		{
			var asDynamic = GetDynamicNode(1173);
			
			var take = asDynamic.Children.Take(2);
			var casted = (IEnumerable<TDocument>)take;

			Assert.AreEqual(2, casted.Count());
			Assert.IsTrue(casted.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1177 }));
		}

		[Test]
		public void Ancestors_Where_Visible()
		{
			var asDynamic = GetDynamicNode(1174);

			var whereVisible = asDynamic.Ancestors().Where("Visible");
			var casted = (IEnumerable<TDocument>)whereVisible;

			Assert.AreEqual(1, casted.Count());
			
		}

		[Test]
		public void Visible()
		{
			var asDynamicHidden = GetDynamicNode(1046);
			var asDynamicVisible = GetDynamicNode(1173);

			Assert.IsFalse(asDynamicHidden.Visible);
			Assert.IsTrue(asDynamicVisible.Visible);
		}

		[Test]
		public void Ensure_TinyMCE_Converted_Type_User_Property()
		{
			var asDynamic = GetDynamicNode(1173);

			Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(asDynamic.Content.GetType()));
			Assert.AreEqual("<div>This is some content</div>", asDynamic.Content.ToString());
		}

		[Test]
		public void Get_Children_With_Pluralized_Alias()
		{
			var asDynamic = GetDynamicNode(1173);

			Action<object> doAssert = d =>
				{
					Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IEnumerable>(d));
					var casted = (IEnumerable<TDocument>)d;
					Assert.AreEqual(2, casted.Count());
				};

			doAssert(asDynamic.Homes); //pluralized alias
			doAssert(asDynamic.homes); //pluralized alias
			doAssert(asDynamic.CustomDocuments); //pluralized alias			
			doAssert(asDynamic.customDocuments); //pluralized alias
		}

		[Test]
		public void GetPropertyValue_Non_Reflected()
		{
			var asDynamic = GetDynamicNode(1174);

			Assert.AreEqual("Custom data with same property name as the member name", asDynamic.GetPropertyValue("creatorName"));
			Assert.AreEqual("Custom data with same property name as the member name", asDynamic.GetPropertyValue("CreatorName"));
		}

		[Test]
		public void GetPropertyValue_Reflected()
		{
			var asDynamic = GetDynamicNode(1174);

			Assert.AreEqual("admin", asDynamic.GetPropertyValue("@creatorName"));
			Assert.AreEqual("admin", asDynamic.GetPropertyValue("@CreatorName"));
		}

		[Test]
		public void Get_User_Property_With_Same_Name_As_Member_Property()
		{
			var asDynamic = GetDynamicNode(1174);

			Assert.AreEqual("Custom data with same property name as the member name", asDynamic.creatorName);

			//because CreatorName is defined on DynamicNode, it will not return the user defined property
			Assert.AreEqual("admin", asDynamic.CreatorName);
		}

		[Test]
		public void Get_Member_Property()
		{
			var asDynamic = GetDynamicNode(1173);
			
			Assert.AreEqual((int) 2, (int) asDynamic.Level);
			Assert.AreEqual((int) 2, (int) asDynamic.level);

			Assert.AreEqual((int) 1046, (int) asDynamic.ParentId);
			Assert.AreEqual((int) 1046, (int) asDynamic.parentId);
		}

		[Test]
		public void Get_Children()
		{
			var asDynamic = GetDynamicNode(1173);

			var children = asDynamic.Children;
			Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IEnumerable>(children));

			var childrenAsList = asDynamic.ChildrenAsList; //test ChildrenAsList too
			Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IEnumerable>(childrenAsList));

			var castChildren = (IEnumerable<TDocument>)children;
			Assert.AreEqual(4, castChildren.Count());

			var castChildrenAsList = (IEnumerable<TDocument>)childrenAsList;
			Assert.AreEqual(4, castChildrenAsList.Count());
		}

		[Test]
		public void Ancestor_Or_Self()
		{
			var asDynamic = GetDynamicNode(1173);

			var result = asDynamic.AncestorOrSelf();

			Assert.IsNotNull(result);

			Assert.AreEqual((int) 1046, (int) result.Id);
		}

		[Test]
		public void Ancestors_Or_Self()
		{
			var asDynamic = GetDynamicNode(1174);

			var result = asDynamic.AncestorsOrSelf();

			Assert.IsNotNull(result);

			var list = (IEnumerable<TDocument>)result;
			Assert.AreEqual(3, list.Count());
			Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
		}

		[Test]
		public void Ancestors()
		{
			var asDynamic = GetDynamicNode(1174);

			var result = asDynamic.Ancestors();

			Assert.IsNotNull(result);

			var list = (IEnumerable<TDocument>)result;
			Assert.AreEqual(2, list.Count());
			Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
		}

		[Test]
		public void Descendants_Or_Self()
		{
			var asDynamic = GetDynamicNode(1046);

			var result = asDynamic.DescendantsOrSelf();

			Assert.IsNotNull(result);

			var list = (IEnumerable<TDocument>)result;
			Assert.AreEqual(9, list.Count());
			Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175, 4444 }));
		}

		[Test]
		public void Descendants()
		{
			var asDynamic = GetDynamicNode(1046);

			var result = asDynamic.Descendants();

			Assert.IsNotNull(result);

			var list = (IEnumerable<TDocument>)result;
			Assert.AreEqual(8, list.Count());
			Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
		}

		[Test]
		public void Up()
		{
			var asDynamic = GetDynamicNode(1173);

			var result = asDynamic.Up();

			Assert.IsNotNull(result);

			Assert.AreEqual((int) 1046, (int) result.Id);
		}

		[Test]
		public void Down()
		{
			var asDynamic = GetDynamicNode(1173);

			var result = asDynamic.Down();

			Assert.IsNotNull(result);

			Assert.AreEqual((int) 1174, (int) result.Id);
		}

		[Test]
		public void Next()
		{
			var asDynamic = GetDynamicNode(1173);

			var result = asDynamic.Next();

			Assert.IsNotNull(result);

			Assert.AreEqual((int) 1175, (int) result.Id);
		}

		[Test]
		public void Next_Without_Sibling()
		{
            var asDynamic = GetDynamicNode(1176);

			Assert.IsNull(asDynamic.Next());
		}

		[Test]
		public void Previous_Without_Sibling()
		{
			var asDynamic = GetDynamicNode(1173);

			Assert.IsNull(asDynamic.Previous());
		}

		[Test]
		public void Previous()
		{
			var asDynamic = GetDynamicNode(1176);

			var result = asDynamic.Previous();

			Assert.IsNotNull(result);

            Assert.AreEqual((int)1178, (int)result.Id);
		}
	}

	/// <summary>
	/// Extension methods used in tests
	/// </summary>
	public static class TestExtensionMethods
	{
		public static bool ContainsValue(this string s, int val)
		{
			return s.Contains(val.ToString());
		}
	}
}